import os
import logging
import json
import time
import ftplib
import sqlite3
import threading
import hashlib
from datetime import datetime
from flask import Flask, render_template, request, redirect, url_for, jsonify, flash
from flask_login import LoginManager, UserMixin, login_user, login_required, logout_user, current_user
from database import db_manager
from sync_manager import SyncManager
from file_syncer import FileSyncer
from ftp_manager import ftp_manager
from logging_config import setup_logging
from websocket_manager import websocket_manager

# 配置日志
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
sync_logger = logging.getLogger('sync')

# 添加自定义时间戳转换器
def adapt_datetime(ts):
    if isinstance(ts, datetime):
        return ts.strftime('%Y-%m-%d %H:%M:%S')
    return ts

def convert_datetime(val):
    try:
        if isinstance(val, bytes):
            val = val.decode()
        return datetime.strptime(val, '%Y-%m-%d %H:%M:%S')
    except (AttributeError, ValueError):
        return None

sqlite3.register_adapter(datetime, adapt_datetime)
sqlite3.register_converter("timestamp", convert_datetime)

# 初始化Flask应用
app = Flask(__name__)
app.secret_key = 'supersecretkey'  # 在生产环境中应该使用更安全的密钥
app.config['DATABASE'] = os.path.join(app.root_path, 'db', 'sync.db')
app.config['IGNORE_PATTERNS'] = ['.svn', '.git', '.DS_Store', 'Thumbs.db']

# 初始化日志系统
root_logger, sync_logger = setup_logging(app.root_path)

# 记录数据库路径
sync_logger.info(f"使用数据库路径: {app.config['DATABASE']}")
sync_logger.info(f"数据库文件是否存在: {os.path.exists(app.config['DATABASE'])}")

# 确保数据库目录存在
os.makedirs(os.path.dirname(app.config['DATABASE']), exist_ok=True)

# 初始化数据库
db_manager.init_app(app.config['DATABASE'])
db_manager.init_db()

# 记录数据库初始化后的状态
sync_logger.info(f"数据库初始化完成，路径: {db_manager.get_db_path()}")

# 初始化Flask-Login
login_manager = LoginManager()
login_manager.init_app(app)
login_manager.login_view = 'login'

# 导入和初始化User类之前，先确保加载了UserMixin
if not hasattr(globals(), 'UserMixin'):
    from flask_login import UserMixin

# 初始化同步管理器
sync_manager = SyncManager()

# 初始化WebSocket
websocket_manager.init_app(app)

# 用户模型
class User(UserMixin):
    def __init__(self, user_id, username):
        self.id = user_id
        self.username = username

    @staticmethod
    def get(user_id):
        with db_manager.get_connection() as db:
            user_data = db.execute(
                'SELECT id, username FROM users WHERE id = ?', (user_id,)
            ).fetchone()
            if user_data:
                return User(user_data['id'], user_data['username'])
            return None

    @staticmethod
    def find_by_username(username):
        with db_manager.get_connection() as db:
            user_data = db.execute(
                'SELECT id, username FROM users WHERE username = ?', (username,)
            ).fetchone()
            if user_data:
                return User(user_data['id'], user_data['username'])
            return None

# Flask-Login加载用户
@login_manager.user_loader
def load_user(user_id):
    return User.get(user_id)

# 路由定义
@app.route('/')
def index():
    if current_user.is_authenticated:
        return redirect(url_for('dashboard'))
    return render_template('index.html')

@app.route('/login', methods=['GET', 'POST'])
def login():
    if current_user.is_authenticated:
        return redirect(url_for('dashboard'))

    if request.method == 'POST':
        username = request.form['username']
        password = request.form['password']
        hashed_password = hashlib.sha256(password.encode()).hexdigest()

        with db_manager.get_connection() as db:
            user_data = db.execute(
                'SELECT id, password FROM users WHERE username = ?', (username,)
            ).fetchone()

            if user_data and user_data['password'] == hashed_password:
                user = User(user_data['id'], username)
                login_user(user)
                return redirect(url_for('dashboard'))
            else:
                flash('用户名或密码错误')

    return render_template('login.html')

@app.route('/register', methods=['GET', 'POST'])
def register():
    if current_user.is_authenticated:
        return redirect(url_for('dashboard'))

    if request.method == 'POST':
        username = request.form['username']
        password = request.form['password']
        confirm_password = request.form['confirm_password']

        if password != confirm_password:
            flash('两次输入的密码不一致')
            return render_template('register.html')

        hashed_password = hashlib.sha256(password.encode()).hexdigest()

        try:
            with db_manager.get_connection() as db:
                db.execute('INSERT INTO users (username, password) VALUES (?, ?)',
                          (username, hashed_password))
                db.commit()
                flash('注册成功，请登录')
                return redirect(url_for('login'))
        except Exception as e:
            flash('用户名已存在')

    return render_template('register.html')

@app.route('/logout')
@login_required
def logout():
    logout_user()
    return redirect(url_for('index'))

@app.route('/dashboard')
@login_required
def dashboard():
    with db_manager.get_connection() as db:
        # 获取用户的同步目录
        sync_dirs = db.execute('''
            SELECT id, local_path, ftp_host, ftp_path, sync_status, 
                   strftime('%Y-%m-%d %H:%M:%S', last_sync) as last_sync, 
                   sync_strategy
            FROM sync_dirs
            WHERE user_id = ?
        ''', (current_user.id,)).fetchall()

        # 获取每个目录的最新同步记录（最近5分钟内的）
        sync_history = {}
        for dir in sync_dirs:
            history = db.execute('''
                SELECT h.filename, h.direction, h.operation, 
                       strftime('%Y-%m-%d %H:%M:%S', h.timestamp) as timestamp, 
                       h.path, d.local_path
                FROM sync_history h
                JOIN sync_dirs d ON h.sync_dir_id = d.id
                WHERE d.id = ? AND h.timestamp >= datetime('now', '-5 minutes')
                ORDER BY h.timestamp DESC
                LIMIT 5
            ''', (dir['id'],)).fetchall()
            sync_history[dir['local_path']] = [dict(row) for row in history]

    return render_template('dashboard.html', 
                         sync_dirs=[dict(row) for row in sync_dirs],
                         sync_history=sync_history)

@app.route('/add_sync_dir', methods=['GET', 'POST'])
@login_required
def add_sync_dir():
    if request.method == 'POST':
        try:
            # 获取表单数据
            local_path = request.form.get('local_path', '')
            ftp_host = request.form['ftp_host']
            ftp_user = request.form['ftp_user']
            ftp_pass = request.form['ftp_pass']
            ftp_path = request.form['ftp_path']
            # 使用默认的同步策略
            sync_strategy = request.form.get('sync_strategy', 'local_priority')

            # 添加同步目录
            sync_dir_id = sync_manager.add_sync_dir(
                user_id=current_user.id,
                local_path=local_path,
                ftp_host=ftp_host,
                ftp_user=ftp_user,
                ftp_pass=ftp_pass,
                ftp_path=ftp_path,
                sync_strategy=sync_strategy,
                ignore_patterns=app.config['IGNORE_PATTERNS']
            )

            flash('同步目录添加成功')
            return redirect(url_for('dashboard'))

        except Exception as e:
            flash(f'添加同步目录失败: {str(e)}')

    return render_template('add_sync_dir.html')

@app.route('/select_folder')
@login_required
def select_folder():
    try:
        import tkinter as tk
        from tkinter import filedialog
        
        root = tk.Tk()
        root.withdraw()
        root.wm_attributes('-topmost', 1)
        folder_path = filedialog.askdirectory(parent=root)
        root.destroy()
        return jsonify({'path': folder_path if folder_path else ''})
    except ImportError:
        # 如果没有tkinter，返回错误信息
        return jsonify({'error': 'tkinter不可用，无法使用文件对话框，请手动输入路径'})
    except Exception as e:
        return jsonify({'error': f'选择文件夹失败: {str(e)}'})

@app.route('/delete_sync_dir/<int:dir_id>')
@login_required
def delete_sync_dir(dir_id):
    try:
        # 验证用户所有权
        with db_manager.get_connection() as db:
            row = db.execute('SELECT user_id FROM sync_dirs WHERE id = ?', (dir_id,)).fetchone()
            if not row or row['user_id'] != current_user.id:
                flash('无权删除此目录')
                return redirect(url_for('dashboard'))

        # 删除同步目录（这会同时停止观察者和清理处理器）
        sync_manager.remove_sync_dir(dir_id)
        
        # 记录删除操作
        sync_logger.info(f"已删除同步目录 ID: {dir_id}")
        
        flash('同步目录已删除')
        return redirect(url_for('dashboard'))

    except Exception as e:
        sync_logger.error(f"删除同步目录失败: {str(e)}")
        flash(f'删除同步目录失败: {str(e)}')
        return redirect(url_for('dashboard'))

@app.route('/sync_now/<int:dir_id>')
@login_required
def sync_now(dir_id):
    try:
        # 验证用户所有权
        with db_manager.get_connection() as db:
            row = db.execute('SELECT user_id FROM sync_dirs WHERE id = ?', (dir_id,)).fetchone()
            if not row or row['user_id'] != current_user.id:
                flash('无权操作此目录')
                return redirect(url_for('dashboard'))

        # 更新同步状态
        sync_manager.update_sync_status(dir_id, 'syncing')
        flash('同步任务已启动')

    except Exception as e:
        flash(f'启动同步失败: {str(e)}')

    return redirect(url_for('dashboard'))

@app.route('/set_sync_strategy/<int:dir_id>/<strategy>')
@login_required
def set_sync_strategy(dir_id, strategy):
    try:
        # 验证用户所有权
        with db_manager.get_connection() as db:
            row = db.execute('SELECT user_id FROM sync_dirs WHERE id = ?', (dir_id,)).fetchone()
            if not row or row['user_id'] != current_user.id:
                flash('无权操作此目录')
                return redirect(url_for('dashboard'))

        # 使用新的update_sync_strategy方法更新同步策略
        success = sync_manager.update_sync_strategy(dir_id, strategy)
        
        if success:
            flash(f'同步策略已更新为 {strategy} 并开始同步')
        else:
            flash('更新同步策略失败，请检查日志')

    except Exception as e:
        flash(f'更新同步策略失败: {str(e)}')

    return redirect(url_for('dashboard'))

@app.route('/force_sync/<int:dir_id>')
@login_required
def force_sync(dir_id):
    try:
        # 验证用户所有权
        with db_manager.get_connection() as db:
            row = db.execute('SELECT user_id FROM sync_dirs WHERE id = ?', (dir_id,)).fetchone()
            if not row or row['user_id'] != current_user.id:
                flash('无权操作此目录')
                return redirect(url_for('dashboard'))

        # 更新同步状态
        sync_manager.update_sync_status(dir_id, 'syncing')
        
        # 执行强制同步
        handler = sync_manager.handlers.get(dir_id)
        if handler and handler.syncer:
            sync_logger.info(f"手动触发目录 {dir_id} 的强制同步")
            # 启动一个线程执行强制同步，避免阻塞请求
            threading.Thread(
                target=lambda: handler.syncer.check_server_changes(force_sync=True),
                daemon=True
            ).start()
            flash('强制同步任务已启动')
        else:
            flash('找不到同步处理器，请重新添加同步目录')

    except Exception as e:
        flash(f'启动强制同步失败: {str(e)}')

    return redirect(url_for('dashboard'))

def format_size(size):
    """格式化文件大小"""
    for unit in ['B', 'KB', 'MB', 'GB', 'TB']:
        if size < 1024.0:
            return f"{size:.1f} {unit}"
        size /= 1024.0
    return f"{size:.1f} PB"

def is_safe_path(base_path, requested_path):
    """验证请求路径是否安全"""
    # 规范化路径
    base_path = os.path.abspath(base_path)
    requested_path = os.path.abspath(os.path.join(base_path, requested_path))
    
    # 检查请求路径是否在基础路径下
    return requested_path.startswith(base_path)

@app.route('/browse_local/<int:dir_id>')
@app.route('/browse_local/<int:dir_id>/<path:subpath>')
@login_required
def browse_local(dir_id, subpath=''):
    try:
        # 验证用户所有权
        with db_manager.get_connection() as db:
            row = db.execute('SELECT local_path, user_id FROM sync_dirs WHERE id = ?', (dir_id,)).fetchone()
            if not row or row['user_id'] != current_user.id:
                flash('无权访问此目录')
                return redirect(url_for('dashboard'))

        base_path = row['local_path']
        current_path = os.path.join(base_path, subpath)

        # 安全性检查
        if not is_safe_path(base_path, subpath):
            flash('无效的路径')
            return redirect(url_for('browse_local', dir_id=dir_id))

        # 确保路径存在
        if not os.path.exists(current_path):
            flash('目录不存在')
            return redirect(url_for('browse_local', dir_id=dir_id))

        # 获取文件列表
        entries = []
        try:
            # 添加返回上级目录的选项（如果不在根目录）
            if subpath:
                parent_path = os.path.dirname(subpath)
                entries.append({
                    'name': '..',
                    'path': parent_path,
                    'is_dir': True,
                    'size': '',
                    'mtime': ''
                })

            # 列出当前目录内容
            for name in os.listdir(current_path):
                # 检查忽略模式
                ignore = False
                for pattern in app.config['IGNORE_PATTERNS']:
                    if pattern in name:
                        ignore = True
                        break

                if not ignore:
                    full_path = os.path.join(current_path, name)
                    rel_path = os.path.join(subpath, name) if subpath else name
                    is_dir = os.path.isdir(full_path)
                    
                    entry = {
                        'name': name + ('/' if is_dir else ''),
                        'path': rel_path,
                        'is_dir': is_dir,
                        'size': '' if is_dir else format_size(os.path.getsize(full_path)),
                        'mtime': datetime.fromtimestamp(os.path.getmtime(full_path))
                    }
                    entries.append(entry)

            # 排序：目录在前，文件在后，按名称排序
            entries.sort(key=lambda x: (not x['is_dir'], x['name'].lower()))

        except Exception as e:
            sync_logger.error(f"读取目录失败: {str(e)}")
            flash(f'读取目录失败: {str(e)}')
            return redirect(url_for('dashboard'))

        return render_template('browse_local.html', 
                            entries=entries,
                            dir_id=dir_id,
                            current_path=subpath,
                            base_path=base_path)

    except Exception as e:
        flash(f'浏览本地目录失败: {str(e)}')
        return redirect(url_for('dashboard'))

def parse_unix_style(line):
    """解析Unix风格的FTP LIST输出"""
    try:
        parts = line.split(None, 8)
        if len(parts) < 9:
            return None
            
        perms, _, owner, group, size, month, day, time_or_year, name = parts
        is_dir = perms.startswith('d')
        date_time = f"{month} {day} {time_or_year}"
        
        return {
            'name': name,
            'is_dir': is_dir,
            'size': size if not is_dir else '',
            'datetime': date_time
        }
    except Exception:
        return None

def parse_windows_style(line):
    """解析Windows风格的FTP LIST输出"""
    try:
        if '<DIR>' in line:
            # Windows DIR格式
            date_str = line[0:17]  # 日期和时间部分
            name = line[39:].strip()  # 文件名部分
            return {
                'name': name,
                'is_dir': True,
                'size': '',
                'datetime': date_str.strip()
            }
        else:
            # Windows 文件格式
            parts = line.strip().split()
            if len(parts) >= 4:
                date_str = ' '.join(parts[0:2])  # 日期和时间
                size = parts[2]  # 文件大小
                name = ' '.join(parts[3:])  # 文件名
                return {
                    'name': name,
                    'is_dir': False,
                    'size': size,
                    'datetime': date_str
                }
    except Exception:
        return None
    return None

def parse_other_style(line):
    """解析其他风格的FTP LIST输出"""
    try:
        parts = line.strip().split()
        if len(parts) >= 2:
            name = parts[-1]
            size = parts[-2] if len(parts) > 2 else ''
            is_dir = '.' not in name or any(c in line.lower() for c in ['<dir>', 'drw', 'dr-'])
            return {
                'name': name,
                'is_dir': is_dir,
                'size': size if not is_dir else '',
                'datetime': ' '.join(parts[:-2]) if len(parts) > 3 else '-'
            }
    except Exception:
        return None
    return None

@app.route('/browse_ftp/<int:dir_id>')
@app.route('/browse_ftp/<int:dir_id>/<path:subpath>')
@login_required
def browse_ftp(dir_id, subpath=''):
    try:
        # 验证用户所有权
        with db_manager.get_connection() as db:
            row = db.execute('''
                SELECT ftp_host, ftp_user, ftp_pass, ftp_path, user_id 
                FROM sync_dirs WHERE id = ?
            ''', (dir_id,)).fetchone()

            if not row or row['user_id'] != current_user.id:
                flash('无权访问此目录')
                return redirect(url_for('dashboard'))

        entries = []
        base_path = row['ftp_path']
        
        # 规范化路径
        if subpath:
            subpath = subpath.replace('\\', '/')
            if subpath.startswith('/'):
                subpath = subpath[1:]
        current_path = os.path.join(base_path, subpath if subpath else '').replace('\\', '/')

        # 连接FTP并获取文件列表
        with ftp_manager.connection(
            row['ftp_host'], 
            row['ftp_user'], 
            row['ftp_pass']
        ) as ftp:
            try:
                # 保存初始目录
                initial_dir = ftp.pwd()
                sync_logger.info(f"当前FTP目录: {initial_dir}")

                # 切换到目标目录
                if current_path:
                    try:
                        sync_logger.info(f"尝试切换到目录: {current_path}")
                        ftp.cwd(current_path)
                        sync_logger.info(f"成功切换到目录: {ftp.pwd()}")
                    except ftplib.error_perm as e:
                        sync_logger.error(f"切换FTP目录失败: {current_path}, 错误: {str(e)}")
                        flash('目录不存在或无法访问')
                        return redirect(url_for('browse_ftp', dir_id=dir_id))

                # 添加返回上级目录的选项（如果不在根目录）
                if subpath:
                    parent_path = os.path.dirname(subpath)
                    entries.append({
                        'name': '..',
                        'path': parent_path,
                        'is_dir': True,
                        'size': '',
                        'datetime': ''
                    })

                # 获取目录列表
                dir_list = []
                raw_list = []

                def handle_line(line):
                    raw_list.append(line)

                try:
                    sync_logger.info("开始获取目录列表...")
                    ftp.retrlines('LIST', handle_line)
                    sync_logger.info(f"获取到原始列表: {raw_list}")

                    # 解析每一行
                    for line in raw_list:
                        if not line.strip() or line.strip() in ['.', '..']:
                            continue

                        # 尝试不同的解析方式
                        entry_info = None
                        for parser in [parse_unix_style, parse_windows_style, parse_other_style]:
                            entry_info = parser(line)
                            if entry_info:
                                break

                        if not entry_info:
                            sync_logger.warning(f"无法解析的行: {line}")
                            continue

                        name = entry_info['name']
                        if any(pattern in name for pattern in app.config['IGNORE_PATTERNS']):
                            continue

                        # 构建相对路径
                        rel_path = os.path.join(subpath, name).replace('\\', '/') if subpath else name

                        # 格式化文件大小
                        size = entry_info['size']
                        if size and size.isdigit():
                            size = format_size(int(size))

                        dir_list.append({
                            'name': name + ('/' if entry_info['is_dir'] else ''),
                            'path': rel_path,
                            'is_dir': entry_info['is_dir'],
                            'size': size,
                            'datetime': entry_info['datetime']
                        })

                except ftplib.error_perm as e:
                    sync_logger.error(f"LIST命令失败: {str(e)}")
                    # 如果LIST失败，尝试NLST
                    try:
                        sync_logger.info("尝试使用NLST命令...")
                        names = ftp.nlst()
                        current_dir = ftp.pwd()
                        
                        for name in names:
                            if name in ['.', '..']:
                                continue
                                
                            if any(pattern in name for pattern in app.config['IGNORE_PATTERNS']):
                                continue
                                
                            rel_path = os.path.join(subpath, name).replace('\\', '/') if subpath else name
                            
                            # 检查是否为目录并获取文件信息
                            try:
                                is_dir = False
                                size = ''
                                try:
                                    # 尝试获取文件大小
                                    size = ftp.size(name)
                                    if size is not None:
                                        size = format_size(size)
                                except:
                                    # 如果获取大小失败，尝试作为目录
                                    try:
                                        ftp.cwd(name)
                                        is_dir = True
                                        ftp.cwd(current_dir)
                                    except:
                                        pass

                                dir_list.append({
                                    'name': name + ('/' if is_dir else ''),
                                    'path': rel_path,
                                    'is_dir': is_dir,
                                    'size': size,
                                    'datetime': '-'
                                })
                            except Exception as e:
                                sync_logger.error(f"处理文件 {name} 时出错: {str(e)}")
                                continue

                        sync_logger.info(f"NLST命令成功，获取到 {len(dir_list)} 个项目")
                    except ftplib.error_perm as e:
                        sync_logger.error(f"所有列表命令都失败了: {str(e)}")
                        flash('无法获取文件列表')
                        return redirect(url_for('dashboard'))

                # 排序：目录在前，文件在后
                dir_list.sort(key=lambda x: (not x['is_dir'], x['name'].lower()))
                entries.extend(dir_list)

                # 确保返回初始目录
                ftp.cwd(initial_dir)

            except Exception as e:
                sync_logger.error(f"浏览FTP目录失败: {str(e)}")
                flash(f'浏览FTP目录失败: {str(e)}')
                return redirect(url_for('dashboard'))

            if not entries:
                sync_logger.warning(f"FTP目录 {current_path} 可能为空或文件列表解析失败")
                flash('目录为空或无法读取文件列表')

            return render_template('browse_ftp.html', 
                                entries=entries,
                                dir_id=dir_id,
                                current_path=subpath,
                                base_path=base_path)

    except Exception as e:
        flash(f'浏览FTP目录失败: {str(e)}')
        return redirect(url_for('dashboard'))

@app.route('/api/test_ftp_connection', methods=['POST'])
def test_ftp_connection():
    """测试FTP连接和服务器设置"""
    try:
        data = request.json
        host = data.get('ftp_host')
        user = data.get('ftp_user')
        password = data.get('ftp_pass')
        
        if not all([host, user, password]):
            return jsonify({"success": False, "message": "请提供完整的FTP服务器信息"})
        
        # 测试FTP服务器设置
        result = ftp_manager.test_server_settings(host, user, password)
        
        if result["connection_successful"]:
            message = "FTP连接测试成功！"
            if "working_encodings" in result and result["working_encodings"]:
                message += f" 推荐编码: {result['recommended_encoding']}"
            return jsonify({"success": True, "message": message, "data": result})
        else:
            return jsonify({"success": False, "message": f"FTP连接测试失败: {result.get('error', '未知错误')}"})
            
    except Exception as e:
        sync_logger.error(f"测试FTP连接失败: {str(e)}")
        return jsonify({"success": False, "message": f"测试失败: {str(e)}"})

@app.route('/api/check_server_changes', methods=['POST'])
def check_server_changes():
    """手动检查服务器变化"""
    try:
        data = request.json
        sync_dir_id = data.get('sync_dir_id')
        force_sync = data.get('force_sync', False)
        
        if not sync_dir_id:
            return jsonify({"success": False, "message": "请提供同步目录ID"})
        
        # 获取同步器并检查服务器变化
        syncer = FileSyncer(sync_dir_id, db_manager.get_db_path())
        sync_success = syncer.check_server_changes(force_sync=force_sync)
        
        if sync_success:
            return jsonify({"success": True, "message": "服务器变化检查完成"})
        else:
            return jsonify({"success": False, "message": "服务器变化检查失败，请检查日志获取详情"})
            
    except Exception as e:
        sync_logger.error(f"检查服务器变化失败: {str(e)}")
        return jsonify({"success": False, "message": f"检查失败: {str(e)}"})

@app.route('/sync_history')
@login_required
def sync_history():
    with db_manager.get_connection() as db:
        # 获取同步目录列表（用于过滤器）
        sync_dirs = db.execute('''
            SELECT id, local_path
            FROM sync_dirs
            WHERE user_id = ?
        ''', (current_user.id,)).fetchall()

        # 获取同步历史
        sync_history = db.execute('''
            SELECT h.filename, h.direction, h.operation, 
                   strftime('%Y-%m-%d %H:%M:%S', h.timestamp) as timestamp, 
                   h.path, d.local_path
            FROM sync_history h
            JOIN sync_dirs d ON h.sync_dir_id = d.id
            WHERE d.user_id = ?
            ORDER BY h.timestamp DESC
        ''', (current_user.id,)).fetchall()

    return render_template('sync_history.html', 
                         sync_dirs=sync_dirs,
                         sync_history=sync_history)

# 启动应用
if __name__ == '__main__':
    # 启动同步管理器
    try:
        # 默认禁用自动同步
        sync_manager.auto_sync_on_start = False
        sync_manager.start()
    except Exception as e:
        logging.error(f"启动同步管理器失败: {str(e)}")
        exit(1)

    # 启动Flask应用（使用WebSocket）
    websocket_manager.socketio.run(app, host='127.0.0.1', port=5011, allow_unsafe_werkzeug=True)